#include "biginteger_p.hpp"

#include <climits>
#include <iomanip>
#include <regex>
#include <sstream>

using bi::BigInteger;

BigInteger::Private::Private(): v(), minus( false ) {
	this->v.push_back( 0 );
}

BigInteger::Private::Private( int i ): v(), minus( i < 0 ) {
	if( this->minus ) {
		i = -i;
	}
	while( i > 0 ) {
		int j = i % 1000;
		this->v.push_back( j );
		i /= 1000;
	}
}

BigInteger::Private::Private( long l ): v(), minus( l < 0 ) {
	if( this->minus ) {
		l = -l;
	}
	while( l > 0 ) {
		int j = l % 1000L;
		this->v.push_back( j );
		l /= 1000L;
	}
}

BigInteger::Private::Private( const char * c ): v(), minus( false ) {
	this->assign( c );
}

BigInteger::Private::Private( const std::string & s ): v(), minus( false ) {
	this->assign( s );
}

void BigInteger::Private::assign( const std::string & s ) {
	this->minus = false;
	this->v.clear();

	std::regex pattern( "^(0)|((-?)([1-9]\\d*))$" );
	std::smatch m;
	if( !std::regex_match( s, m, pattern ) ) {
		throw;
	}
	if( m[1].matched ) {
		this->v.push_back( 0 );
	} else {
		if( m[3].length() != 0 ) {
			this->minus = true;
		}
		std::string buffer( m[4] );
		// add padding to 3 multiple
		if( s.size() % 3 != 0 ) {
			buffer.insert( 0, 3 - s.size() % 3, ' ' );
		}
		for( std::size_t i = 0; i < buffer.size(); i += 3 ) {
			std::istringstream sin( buffer.substr( i, 3 ) );
			int j = -1;
			sin >> j;
			this->v.insert( this->v.begin(), j );
		}
	}
}

void BigInteger::Private::normalize() {
	while( this->v.size() > 1 && this->v.back() == 0 ) {
		this->v.pop_back();
	}
	if( this->v.size() == 1 && this->v[0] == 0 ) {
		this->minus = false;
	}
}

BigInteger::BigInteger(): p_( new Private ) {
}

BigInteger::BigInteger( int i ): p_( new Private( i ) ) {
}

BigInteger::BigInteger( long l ): p_( new Private( l ) ) {
}

BigInteger::BigInteger( const char * c ): p_( new Private( c ) ) {
}

BigInteger::BigInteger( const std::string & s ): p_( new Private( s ) ) {
}

BigInteger::BigInteger( const BigInteger & that ): p_( new Private( *that.p_ ) ) {
}

BigInteger::BigInteger( BigInteger && that ): p_( that.p_.release() ) {
}

BigInteger::~BigInteger() {
}

BigInteger & BigInteger::operator =( const BigInteger & that ) {
	if( &that == this ) {
		return *this;
	}

	this->p_->minus = that.p_->minus;
	this->p_->v = that.p_->v;

	return *this;
}

BigInteger & BigInteger::operator =( BigInteger && that ) {
	this->p_.swap( that.p_ );

	return *this;
}

BigInteger & BigInteger::operator +=( const BigInteger & that ) {
	if( this->p_->minus != that.p_->minus ) {
		if( !this->p_->minus ) {
			return *this -= -that;
		} else {
			return *this = that - -*this;
		}
	}

	int carry = 0;
	std::size_t minlen = std::min( this->p_->v.size(), that.p_->v.size() );
	std::size_t i = 0;
	for( i = 0; i < minlen; ++i ) {
		this->p_->v[i] += that.p_->v[i] + carry;
		carry = this->p_->v[i] / 1000;
		this->p_->v[i] %= 1000;
	}
	if( this->p_->v.size() > that.p_->v.size() ) {
		this->p_->v[i] += carry;
	} else if( this->p_->v.size() < that.p_->v.size() ) {
		auto it = that.p_->v.begin();
		std::advance( it, i );
		this->p_->v.insert( this->p_->v.end(), it, that.p_->v.end() );
		this->p_->v[i] += carry;
	} else if( carry != 0 ) {
		this->p_->v.push_back( carry );
	}

	return *this;
}

BigInteger & BigInteger::operator -=( const BigInteger & that ) {
	if( this->p_->minus != that.p_->minus ) {
		return *this += -that;
	}

	auto bigger = &this->p_->v;
	auto little = &that.p_->v;
	if( *this < that ) {
		this->p_->minus = !this->p_->minus;
		std::swap( bigger, little );
	}
	std::size_t i = 0;
	for( i = 0; i < little->size(); ++i ) {
		this->p_->v[i] = bigger->at( i ) - little->at( i );
		if( this->p_->v[i] < 0 ) {
			this->p_->v[i+1] -= 1;
			this->p_->v[i] += 1000;
		}
	}

	this->p_->normalize();
	return *this;
}

BigInteger BigInteger::operator -() const {
	BigInteger tmp( *this );
	if( !( tmp.p_->v.size() == 1 && tmp.p_->v[0] == 0 ) ) {
		tmp.p_->minus = !tmp.p_->minus;
	}
	return std::move( tmp );
}

namespace bi {
	template<>
	int BigInteger::to< int >() const {
		if( *this > INT_MAX ) {
			return INT_MAX;
		}
		if( *this < INT_MIN ) {
			return INT_MIN;
		}
		int tmp = 0;
		for( std::size_t i = 0; i < this->p_->v.size(); ++i ) {
			tmp += this->p_->v[i] * static_cast< int >( std::pow( 1000, static_cast< double >( i ) ) );
		}
		return tmp;
	}
}

bool bi::operator <( const BigInteger & a, const BigInteger & b ) {
	if( a.p_->minus && !b.p_->minus ) {
		return true;
	} else if( !a.p_->minus && b.p_->minus ) {
		return false;
	}
	if( !a.p_->minus ) {
		if( a.p_->v.size() == b.p_->v.size() ) {
			return a.p_->v.back() < b.p_->v.back();
		}
		return a.p_->v.size() < b.p_->v.size();
	}
	if( a.p_->v.size() == b.p_->v.size() ) {
		return a.p_->v.back() > b.p_->v.back();
	}
	return a.p_->v.size() > b.p_->v.size();
}

bool bi::operator <=( const BigInteger & a, const BigInteger & b ) {
	return !( a > b );
}

bool bi::operator ==( const BigInteger & a, const BigInteger & b ) {
	return a.p_->minus == b.p_->minus && a.p_->v == b.p_->v;
}

bool bi::operator >=( const BigInteger & a, const BigInteger & b ) {
	return !( a < b );
}

bool bi::operator >( const BigInteger & a, const BigInteger & b ) {
	return b < a;
}

bool bi::operator !=( const BigInteger & a, const BigInteger & b ) {
	return !( a == b );
}

BigInteger bi::operator +( const BigInteger & a, const BigInteger & b ) {
	return std::move( BigInteger( a ) += b );
}

BigInteger bi::operator +( const BigInteger & a, BigInteger && b ) {
	return std::move( b += a );
}

BigInteger bi::operator +( BigInteger && a, const BigInteger & b ) {
	return std::move( a += b );
}

BigInteger bi::operator +( BigInteger && a, BigInteger && b ) {
	return std::move( a += b );
}

BigInteger bi::operator -( const BigInteger & a, const BigInteger & b ) {
	return std::move( BigInteger( a ) -= b );
}

BigInteger bi::operator -( const BigInteger & a, BigInteger && b ) {
	return std::move( BigInteger( a ) -= b );
}

BigInteger bi::operator -( BigInteger && a, const BigInteger & b ) {
	return std::move( a -= b );
}

BigInteger bi::operator -( BigInteger && a, BigInteger && b ) {
	return std::move( a -= b );
}

std::ostream & bi::operator <<( std::ostream & out, const BigInteger & self ) {
	if( self.p_->minus ) {
		out << '-';
	}
	auto it = self.p_->v.rbegin();
	out << *it;
	for( std::advance( it, 1 ); it != self.p_->v.rend(); ++it ) {
		out << std::setfill( '0' ) << std::setw( 3 ) << *it;
	}
	return out;
}
